1 // Written in the D programming language. 2 3 /** 4 This is a submodule of $(MREF std, math). 5 6 It contains hardware support for floating point numbers. 7 8 Copyright: Copyright The D Language Foundation 2000 - 2011. 9 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 10 Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, 11 Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger 12 Source: $(PHOBOSSRC std/math/hardware.d) 13 */ 14 15 module std.math.hardware; 16 17 static import core.stdc.fenv; 18 19 version (X86) version = X86_Any; 20 version (X86_64) version = X86_Any; 21 version (PPC) version = PPC_Any; 22 version (PPC64) version = PPC_Any; 23 version (MIPS32) version = MIPS_Any; 24 version (MIPS64) version = MIPS_Any; 25 version (AArch64) version = ARM_Any; 26 version (ARM) version = ARM_Any; 27 version (S390) version = IBMZ_Any; 28 version (SPARC) version = SPARC_Any; 29 version (SPARC64) version = SPARC_Any; 30 version (SystemZ) version = IBMZ_Any; 31 version (RISCV32) version = RISCV_Any; 32 version (RISCV64) version = RISCV_Any; 33 34 version (D_InlineAsm_X86) version = InlineAsm_X86_Any; 35 version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any; 36 37 version (X86_64) version = StaticallyHaveSSE; 38 version (X86) version (OSX) version = StaticallyHaveSSE; 39 40 version (StaticallyHaveSSE) 41 { 42 private enum bool haveSSE = true; 43 } 44 else version (X86) 45 { 46 version(CustomRuntimeTest) 47 { 48 private immutable bool haveSSE = false; 49 } 50 else 51 { 52 static import core.cpuid; 53 private alias haveSSE = core.cpuid.sse; 54 } 55 } 56 57 version (D_SoftFloat) 58 { 59 // Some soft float implementations may support IEEE floating flags. 60 // The implementation here supports hardware flags only and is so currently 61 // only available for supported targets. 62 } 63 else version (X86_Any) version = IeeeFlagsSupport; 64 else version (PPC_Any) version = IeeeFlagsSupport; 65 else version (RISCV_Any) version = IeeeFlagsSupport; 66 else version (MIPS_Any) version = IeeeFlagsSupport; 67 else version (ARM_Any) version = IeeeFlagsSupport; 68 69 // Struct FloatingPointControl is only available if hardware FP units are available. 70 version (D_HardFloat) 71 { 72 // FloatingPointControl.clearExceptions() depends on version IeeeFlagsSupport 73 version (IeeeFlagsSupport) version = FloatingPointControlSupport; 74 } 75 76 version (IeeeFlagsSupport) 77 { 78 79 /** IEEE exception status flags ('sticky bits') 80 81 These flags indicate that an exceptional floating-point condition has occurred. 82 They indicate that a NaN or an infinity has been generated, that a result 83 is inexact, or that a signalling NaN has been encountered. If floating-point 84 exceptions are enabled (unmasked), a hardware exception will be generated 85 instead of setting these flags. 86 */ 87 struct IeeeFlags 88 { 89 nothrow @nogc: 90 91 private: 92 // The x87 FPU status register is 16 bits. 93 // The Pentium SSE2 status register is 32 bits. 94 // The ARM and PowerPC FPSCR is a 32-bit register. 95 // The SPARC FSR is a 32bit register (64 bits for SPARC 7 & 8, but high bits are uninteresting). 96 // The RISC-V (32 & 64 bit) fcsr is 32-bit register. 97 uint flags; 98 99 version (CRuntime_Microsoft) 100 { 101 // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv). 102 // Applies to both x87 status word (16 bits) and SSE2 status word(32 bits). 103 enum : int 104 { 105 INEXACT_MASK = 0x20, 106 UNDERFLOW_MASK = 0x10, 107 OVERFLOW_MASK = 0x08, 108 DIVBYZERO_MASK = 0x04, 109 INVALID_MASK = 0x01, 110 111 EXCEPTIONS_MASK = 0b11_1111 112 } 113 // Don't bother about subnormals, they are not supported on most CPUs. 114 // SUBNORMAL_MASK = 0x02; 115 } 116 else 117 { 118 enum : int 119 { 120 INEXACT_MASK = core.stdc.fenv.FE_INEXACT, 121 UNDERFLOW_MASK = core.stdc.fenv.FE_UNDERFLOW, 122 OVERFLOW_MASK = core.stdc.fenv.FE_OVERFLOW, 123 DIVBYZERO_MASK = core.stdc.fenv.FE_DIVBYZERO, 124 INVALID_MASK = core.stdc.fenv.FE_INVALID, 125 EXCEPTIONS_MASK = core.stdc.fenv.FE_ALL_EXCEPT, 126 } 127 } 128 129 static uint getIeeeFlags() @trusted pure 130 { 131 version (InlineAsm_X86_Any) 132 { 133 ushort sw; 134 asm pure nothrow @nogc { fstsw sw; } 135 136 // OR the result with the SSE2 status register (MXCSR). 137 if (haveSSE) 138 { 139 uint mxcsr; 140 asm pure nothrow @nogc { stmxcsr mxcsr; } 141 return (sw | mxcsr) & EXCEPTIONS_MASK; 142 } 143 else return sw & EXCEPTIONS_MASK; 144 } 145 else version (SPARC) 146 { 147 /* 148 int retval; 149 asm pure nothrow @nogc { st %fsr, retval; } 150 return retval; 151 */ 152 assert(0, "Not yet supported"); 153 } 154 else version (ARM) 155 { 156 assert(false, "Not yet supported."); 157 } 158 else version (RISCV_Any) 159 { 160 mixin(` 161 uint result = void; 162 asm pure nothrow @nogc 163 { 164 "frflags %0" : "=r" (result); 165 } 166 return result; 167 `); 168 } 169 else 170 assert(0, "Not yet supported"); 171 } 172 173 static void resetIeeeFlags() @trusted 174 { 175 version (InlineAsm_X86_Any) 176 { 177 asm nothrow @nogc 178 { 179 fnclex; 180 } 181 182 // Also clear exception flags in MXCSR, SSE's control register. 183 if (haveSSE) 184 { 185 uint mxcsr; 186 asm nothrow @nogc { stmxcsr mxcsr; } 187 mxcsr &= ~EXCEPTIONS_MASK; 188 asm nothrow @nogc { ldmxcsr mxcsr; } 189 } 190 } 191 else version (RISCV_Any) 192 { 193 mixin(` 194 uint newValues = 0x0; 195 asm pure nothrow @nogc 196 { 197 "fsflags %0" : : "r" (newValues); 198 } 199 `); 200 } 201 else 202 { 203 /* SPARC: 204 int tmpval; 205 asm pure nothrow @nogc { st %fsr, tmpval; } 206 tmpval &=0xFFFF_FC00; 207 asm pure nothrow @nogc { ld tmpval, %fsr; } 208 */ 209 assert(0, "Not yet supported"); 210 } 211 } 212 213 public: 214 /** 215 * The result cannot be represented exactly, so rounding occurred. 216 * Example: `x = sin(0.1);` 217 */ 218 @property bool inexact() @safe const { return (flags & INEXACT_MASK) != 0; } 219 220 /** 221 * A zero was generated by underflow 222 * Example: `x = real.min*real.epsilon/2;` 223 */ 224 @property bool underflow() @safe const { return (flags & UNDERFLOW_MASK) != 0; } 225 226 /** 227 * An infinity was generated by overflow 228 * Example: `x = real.max*2;` 229 */ 230 @property bool overflow() @safe const { return (flags & OVERFLOW_MASK) != 0; } 231 232 /** 233 * An infinity was generated by division by zero 234 * Example: `x = 3/0.0;` 235 */ 236 @property bool divByZero() @safe const { return (flags & DIVBYZERO_MASK) != 0; } 237 238 /** 239 * A machine NaN was generated. 240 * Example: `x = real.infinity * 0.0;` 241 */ 242 @property bool invalid() @safe const { return (flags & INVALID_MASK) != 0; } 243 } 244 245 /// 246 version (StdDdoc) 247 @safe unittest 248 { 249 import std.math.traits : isNaN; 250 251 static void func() { 252 int a = 10 * 10; 253 } 254 real a = 3.5; 255 // Set all the flags to zero 256 resetIeeeFlags(); 257 assert(!ieeeFlags.divByZero); 258 // Perform a division by zero. 259 a /= 0.0L; 260 assert(a == real.infinity); 261 assert(ieeeFlags.divByZero); 262 // Create a NaN 263 a *= 0.0L; 264 assert(ieeeFlags.invalid); 265 assert(isNaN(a)); 266 267 // Check that calling func() has no effect on the 268 // status flags. 269 IeeeFlags f = ieeeFlags; 270 func(); 271 assert(ieeeFlags == f); 272 } 273 274 @safe unittest 275 { 276 import std.math.traits : isNaN; 277 278 static void func() { 279 int a = 10 * 10; 280 } 281 real a = 3.5; 282 // Set all the flags to zero 283 resetIeeeFlags(); 284 assert(!ieeeFlags.divByZero); 285 // Perform a division by zero. 286 a = forceDivOp(a, 0.0L); 287 assert(a == real.infinity); 288 assert(ieeeFlags.divByZero); 289 // Create a NaN 290 a = forceMulOp(a, 0.0L); 291 assert(ieeeFlags.invalid); 292 assert(isNaN(a)); 293 294 // Check that calling func() has no effect on the 295 // status flags. 296 IeeeFlags f = ieeeFlags; 297 func(); 298 assert(ieeeFlags == f); 299 } 300 301 @safe unittest 302 { 303 import std.meta : AliasSeq; 304 305 static struct Test 306 { 307 void delegate() @trusted action; 308 bool function() @trusted ieeeCheck; 309 } 310 311 static foreach (T; AliasSeq!(float, double, real)) 312 {{ 313 T x; // Needs to be here to avoid `call without side effects` warning. 314 auto tests = [ 315 Test( 316 () { x = forceAddOp!T(1, 0.1L); }, 317 () => ieeeFlags.inexact 318 ), 319 Test( 320 () { x = forceDivOp!T(T.min_normal, T.max); }, 321 () => ieeeFlags.underflow 322 ), 323 Test( 324 () { x = forceAddOp!T(T.max, T.max); }, 325 () => ieeeFlags.overflow 326 ), 327 Test( 328 () { x = forceDivOp!T(1, 0); }, 329 () => ieeeFlags.divByZero 330 ), 331 Test( 332 () { x = forceDivOp!T(0, 0); }, 333 () => ieeeFlags.invalid 334 ) 335 ]; 336 foreach (test; tests) 337 { 338 resetIeeeFlags(); 339 assert(!test.ieeeCheck()); 340 test.action(); 341 assert(test.ieeeCheck()); 342 } 343 }} 344 } 345 346 /// Set all of the floating-point status flags to false. 347 void resetIeeeFlags() @trusted nothrow @nogc 348 { 349 IeeeFlags.resetIeeeFlags(); 350 } 351 352 /// 353 version (StdDdoc) 354 @safe unittest 355 { 356 resetIeeeFlags(); 357 real a = 3.5; 358 a /= 0.0L; 359 assert(a == real.infinity); 360 assert(ieeeFlags.divByZero); 361 362 resetIeeeFlags(); 363 assert(!ieeeFlags.divByZero); 364 } 365 366 @safe unittest 367 { 368 resetIeeeFlags(); 369 real a = 3.5; 370 a = forceDivOp(a, 0.0L); 371 assert(a == real.infinity); 372 assert(ieeeFlags.divByZero); 373 374 resetIeeeFlags(); 375 assert(!ieeeFlags.divByZero); 376 } 377 378 /// Returns: snapshot of the current state of the floating-point status flags 379 @property IeeeFlags ieeeFlags() @trusted pure nothrow @nogc 380 { 381 return IeeeFlags(IeeeFlags.getIeeeFlags()); 382 } 383 384 /// 385 version (StdDdoc) 386 @safe nothrow unittest 387 { 388 import std.math.traits : isNaN; 389 390 resetIeeeFlags(); 391 real a = 3.5; 392 393 a /= 0.0L; 394 assert(a == real.infinity); 395 assert(ieeeFlags.divByZero); 396 397 a *= 0.0L; 398 assert(isNaN(a)); 399 assert(ieeeFlags.invalid); 400 } 401 402 @safe nothrow unittest 403 { 404 import std.math.traits : isNaN; 405 406 resetIeeeFlags(); 407 real a = 3.5; 408 409 a = forceDivOp(a, 0.0L); 410 assert(a == real.infinity); 411 assert(ieeeFlags.divByZero); 412 413 a = forceMulOp(a, 0.0L); 414 assert(isNaN(a)); 415 assert(ieeeFlags.invalid); 416 } 417 418 } // IeeeFlagsSupport 419 420 421 version (FloatingPointControlSupport) 422 { 423 424 /** Control the Floating point hardware 425 426 Change the IEEE754 floating-point rounding mode and the floating-point 427 hardware exceptions. 428 429 By default, the rounding mode is roundToNearest and all hardware exceptions 430 are disabled. For most applications, debugging is easier if the $(I division 431 by zero), $(I overflow), and $(I invalid operation) exceptions are enabled. 432 These three are combined into a $(I severeExceptions) value for convenience. 433 Note in particular that if $(I invalidException) is enabled, a hardware trap 434 will be generated whenever an uninitialized floating-point variable is used. 435 436 All changes are temporary. The previous state is restored at the 437 end of the scope. 438 439 440 Example: 441 ---- 442 { 443 FloatingPointControl fpctrl; 444 445 // Enable hardware exceptions for division by zero, overflow to infinity, 446 // invalid operations, and uninitialized floating-point variables. 447 fpctrl.enableExceptions(FloatingPointControl.severeExceptions); 448 449 // This will generate a hardware exception, if x is a 450 // default-initialized floating point variable: 451 real x; // Add `= 0` or even `= real.nan` to not throw the exception. 452 real y = x * 3.0; 453 454 // The exception is only thrown for default-uninitialized NaN-s. 455 // NaN-s with other payload are valid: 456 real z = y * real.nan; // ok 457 458 // The set hardware exceptions and rounding modes will be disabled when 459 // leaving this scope. 460 } 461 ---- 462 463 */ 464 struct FloatingPointControl 465 { 466 nothrow @nogc: 467 468 alias RoundingMode = uint; /// 469 470 version (StdDdoc) 471 { 472 enum : RoundingMode 473 { 474 /** IEEE rounding modes. 475 * The default mode is roundToNearest. 476 * 477 * roundingMask = A mask of all rounding modes. 478 */ 479 roundToNearest, 480 roundDown, /// ditto 481 roundUp, /// ditto 482 roundToZero, /// ditto 483 roundingMask, /// ditto 484 } 485 } 486 else version (CRuntime_Microsoft) 487 { 488 // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv). 489 enum : RoundingMode 490 { 491 roundToNearest = 0x0000, 492 roundDown = 0x0400, 493 roundUp = 0x0800, 494 roundToZero = 0x0C00, 495 roundingMask = roundToNearest | roundDown 496 | roundUp | roundToZero, 497 } 498 } 499 else 500 { 501 enum : RoundingMode 502 { 503 roundToNearest = core.stdc.fenv.FE_TONEAREST, 504 roundDown = core.stdc.fenv.FE_DOWNWARD, 505 roundUp = core.stdc.fenv.FE_UPWARD, 506 roundToZero = core.stdc.fenv.FE_TOWARDZERO, 507 roundingMask = roundToNearest | roundDown 508 | roundUp | roundToZero, 509 } 510 } 511 512 /*** 513 * Change the floating-point hardware rounding mode 514 * 515 * Changing the rounding mode in the middle of a function can interfere 516 * with optimizations of floating point expressions, as the optimizer assumes 517 * that the rounding mode does not change. 518 * It is best to change the rounding mode only at the 519 * beginning of the function, and keep it until the function returns. 520 * It is also best to add the line: 521 * --- 522 * pragma(inline, false); 523 * --- 524 * as the first line of the function so it will not get inlined. 525 * Params: 526 * newMode = the new rounding mode 527 */ 528 @property void rounding(RoundingMode newMode) @trusted 529 { 530 initialize(); 531 setControlState((getControlState() & (-1 - roundingMask)) | (newMode & roundingMask)); 532 } 533 534 /// Returns: the currently active rounding mode 535 @property static RoundingMode rounding() @trusted pure 536 { 537 return cast(RoundingMode)(getControlState() & roundingMask); 538 } 539 540 alias ExceptionMask = uint; /// 541 542 version (StdDdoc) 543 { 544 enum : ExceptionMask 545 { 546 /** IEEE hardware exceptions. 547 * By default, all exceptions are masked (disabled). 548 * 549 * severeExceptions = The overflow, division by zero, and invalid 550 * exceptions. 551 */ 552 subnormalException, 553 inexactException, /// ditto 554 underflowException, /// ditto 555 overflowException, /// ditto 556 divByZeroException, /// ditto 557 invalidException, /// ditto 558 severeExceptions, /// ditto 559 allExceptions, /// ditto 560 } 561 } 562 else version (ARM_Any) 563 { 564 enum : ExceptionMask 565 { 566 subnormalException = 0x8000, 567 inexactException = 0x1000, 568 underflowException = 0x0800, 569 overflowException = 0x0400, 570 divByZeroException = 0x0200, 571 invalidException = 0x0100, 572 severeExceptions = overflowException | divByZeroException 573 | invalidException, 574 allExceptions = severeExceptions | underflowException 575 | inexactException | subnormalException, 576 } 577 } 578 else version (PPC_Any) 579 { 580 enum : ExceptionMask 581 { 582 inexactException = 0x0008, 583 divByZeroException = 0x0010, 584 underflowException = 0x0020, 585 overflowException = 0x0040, 586 invalidException = 0x0080, 587 severeExceptions = overflowException | divByZeroException 588 | invalidException, 589 allExceptions = severeExceptions | underflowException 590 | inexactException, 591 } 592 } 593 else version (RISCV_Any) 594 { 595 enum : ExceptionMask 596 { 597 inexactException = 0x01, 598 divByZeroException = 0x08, 599 underflowException = 0x02, 600 overflowException = 0x04, 601 invalidException = 0x10, 602 severeExceptions = overflowException | divByZeroException 603 | invalidException, 604 allExceptions = severeExceptions | underflowException 605 | inexactException, 606 } 607 } 608 else version (HPPA) 609 { 610 enum : ExceptionMask 611 { 612 inexactException = 0x01, 613 underflowException = 0x02, 614 overflowException = 0x04, 615 divByZeroException = 0x08, 616 invalidException = 0x10, 617 severeExceptions = overflowException | divByZeroException 618 | invalidException, 619 allExceptions = severeExceptions | underflowException 620 | inexactException, 621 } 622 } 623 else version (MIPS_Any) 624 { 625 enum : ExceptionMask 626 { 627 inexactException = 0x0080, 628 divByZeroException = 0x0400, 629 overflowException = 0x0200, 630 underflowException = 0x0100, 631 invalidException = 0x0800, 632 severeExceptions = overflowException | divByZeroException 633 | invalidException, 634 allExceptions = severeExceptions | underflowException 635 | inexactException, 636 } 637 } 638 else version (SPARC_Any) 639 { 640 enum : ExceptionMask 641 { 642 inexactException = 0x0800000, 643 divByZeroException = 0x1000000, 644 overflowException = 0x4000000, 645 underflowException = 0x2000000, 646 invalidException = 0x8000000, 647 severeExceptions = overflowException | divByZeroException 648 | invalidException, 649 allExceptions = severeExceptions | underflowException 650 | inexactException, 651 } 652 } 653 else version (IBMZ_Any) 654 { 655 enum : ExceptionMask 656 { 657 inexactException = 0x08000000, 658 divByZeroException = 0x40000000, 659 overflowException = 0x20000000, 660 underflowException = 0x10000000, 661 invalidException = 0x80000000, 662 severeExceptions = overflowException | divByZeroException 663 | invalidException, 664 allExceptions = severeExceptions | underflowException 665 | inexactException, 666 } 667 } 668 else version (X86_Any) 669 { 670 enum : ExceptionMask 671 { 672 inexactException = 0x20, 673 underflowException = 0x10, 674 overflowException = 0x08, 675 divByZeroException = 0x04, 676 subnormalException = 0x02, 677 invalidException = 0x01, 678 severeExceptions = overflowException | divByZeroException 679 | invalidException, 680 allExceptions = severeExceptions | underflowException 681 | inexactException | subnormalException, 682 } 683 } 684 else 685 static assert(false, "Not implemented for this architecture"); 686 687 version (ARM_Any) 688 { 689 static bool hasExceptionTraps_impl() @safe 690 { 691 auto oldState = getControlState(); 692 // If exceptions are not supported, we set the bit but read it back as zero 693 // https://sourceware.org/ml/libc-ports/2012-06/msg00091.html 694 setControlState(oldState | divByZeroException); 695 immutable result = (getControlState() & allExceptions) != 0; 696 setControlState(oldState); 697 return result; 698 } 699 } 700 701 /// Returns: true if the current FPU supports exception trapping 702 @property static bool hasExceptionTraps() @safe pure 703 { 704 version (X86_Any) 705 return true; 706 else version (PPC_Any) 707 return true; 708 else version (MIPS_Any) 709 return true; 710 else version (ARM_Any) 711 { 712 // The hasExceptionTraps_impl function is basically pure, 713 // as it restores all global state 714 auto fptr = ( () @trusted => cast(bool function() @safe 715 pure nothrow @nogc)&hasExceptionTraps_impl)(); 716 return fptr(); 717 } 718 else 719 assert(0, "Not yet supported"); 720 } 721 722 /// Enable (unmask) specific hardware exceptions. Multiple exceptions may be ORed together. 723 void enableExceptions(ExceptionMask exceptions) @trusted 724 { 725 assert(hasExceptionTraps); 726 initialize(); 727 version (X86_Any) 728 setControlState(getControlState() & ~(exceptions & allExceptions)); 729 else 730 setControlState(getControlState() | (exceptions & allExceptions)); 731 } 732 733 /// Disable (mask) specific hardware exceptions. Multiple exceptions may be ORed together. 734 void disableExceptions(ExceptionMask exceptions) @trusted 735 { 736 assert(hasExceptionTraps); 737 initialize(); 738 version (X86_Any) 739 setControlState(getControlState() | (exceptions & allExceptions)); 740 else 741 setControlState(getControlState() & ~(exceptions & allExceptions)); 742 } 743 744 /// Returns: the exceptions which are currently enabled (unmasked) 745 @property static ExceptionMask enabledExceptions() @trusted pure 746 { 747 assert(hasExceptionTraps); 748 version (X86_Any) 749 return (getControlState() & allExceptions) ^ allExceptions; 750 else 751 return (getControlState() & allExceptions); 752 } 753 754 /// Clear all pending exceptions, then restore the original exception state and rounding mode. 755 ~this() @trusted 756 { 757 clearExceptions(); 758 if (initialized) 759 setControlState(savedState); 760 } 761 762 private: 763 ControlState savedState; 764 765 bool initialized = false; 766 767 version (ARM_Any) 768 { 769 alias ControlState = uint; 770 } 771 else version (HPPA) 772 { 773 alias ControlState = uint; 774 } 775 else version (PPC_Any) 776 { 777 alias ControlState = uint; 778 } 779 else version (RISCV_Any) 780 { 781 alias ControlState = uint; 782 } 783 else version (MIPS_Any) 784 { 785 alias ControlState = uint; 786 } 787 else version (SPARC_Any) 788 { 789 alias ControlState = ulong; 790 } 791 else version (IBMZ_Any) 792 { 793 alias ControlState = uint; 794 } 795 else version (X86_Any) 796 { 797 alias ControlState = ushort; 798 } 799 else 800 static assert(false, "Not implemented for this architecture"); 801 802 void initialize() @safe 803 { 804 // BUG: This works around the absence of this() constructors. 805 if (initialized) return; 806 clearExceptions(); 807 savedState = getControlState(); 808 initialized = true; 809 } 810 811 // Clear all pending exceptions 812 static void clearExceptions() @safe 813 { 814 version (IeeeFlagsSupport) 815 resetIeeeFlags(); 816 else 817 static assert(false, "Not implemented for this architecture"); 818 } 819 820 // Read from the control register 821 package(std.math) static ControlState getControlState() @trusted pure 822 { 823 version (D_InlineAsm_X86) 824 { 825 short cont; 826 asm pure nothrow @nogc 827 { 828 xor EAX, EAX; 829 fstcw cont; 830 } 831 return cont; 832 } 833 else version (D_InlineAsm_X86_64) 834 { 835 short cont; 836 asm pure nothrow @nogc 837 { 838 xor RAX, RAX; 839 fstcw cont; 840 } 841 return cont; 842 } 843 else version (RISCV_Any) 844 { 845 mixin(` 846 ControlState cont; 847 asm pure nothrow @nogc 848 { 849 "frcsr %0" : "=r" (cont); 850 } 851 return cont; 852 `); 853 } 854 else 855 assert(0, "Not yet supported"); 856 } 857 858 // Set the control register 859 package(std.math) static void setControlState(ControlState newState) @trusted 860 { 861 version (InlineAsm_X86_Any) 862 { 863 asm nothrow @nogc 864 { 865 fclex; 866 fldcw newState; 867 } 868 869 // Also update MXCSR, SSE's control register. 870 if (haveSSE) 871 { 872 uint mxcsr; 873 asm nothrow @nogc { stmxcsr mxcsr; } 874 875 /* In the FPU control register, rounding mode is in bits 10 and 876 11. In MXCSR it's in bits 13 and 14. */ 877 mxcsr &= ~(roundingMask << 3); // delete old rounding mode 878 mxcsr |= (newState & roundingMask) << 3; // write new rounding mode 879 880 /* In the FPU control register, masks are bits 0 through 5. 881 In MXCSR they're 7 through 12. */ 882 mxcsr &= ~(allExceptions << 7); // delete old masks 883 mxcsr |= (newState & allExceptions) << 7; // write new exception masks 884 885 asm nothrow @nogc { ldmxcsr mxcsr; } 886 } 887 } 888 else version (RISCV_Any) 889 { 890 mixin(` 891 asm pure nothrow @nogc 892 { 893 "fscsr %0" : : "r" (newState); 894 } 895 `); 896 } 897 else 898 assert(0, "Not yet supported"); 899 } 900 } 901 902 /// 903 @safe unittest 904 { 905 import std.math.rounding : lrint; 906 907 FloatingPointControl fpctrl; 908 909 fpctrl.rounding = FloatingPointControl.roundDown; 910 assert(lrint(1.5) == 1.0); 911 912 fpctrl.rounding = FloatingPointControl.roundUp; 913 assert(lrint(1.4) == 2.0); 914 915 fpctrl.rounding = FloatingPointControl.roundToNearest; 916 assert(lrint(1.5) == 2.0); 917 } 918 919 @safe unittest 920 { 921 void ensureDefaults() 922 { 923 assert(FloatingPointControl.rounding 924 == FloatingPointControl.roundToNearest); 925 if (FloatingPointControl.hasExceptionTraps) 926 assert(FloatingPointControl.enabledExceptions == 0); 927 } 928 929 { 930 FloatingPointControl ctrl; 931 } 932 ensureDefaults(); 933 934 { 935 FloatingPointControl ctrl; 936 ctrl.rounding = FloatingPointControl.roundDown; 937 assert(FloatingPointControl.rounding == FloatingPointControl.roundDown); 938 } 939 ensureDefaults(); 940 941 if (FloatingPointControl.hasExceptionTraps) 942 { 943 FloatingPointControl ctrl; 944 ctrl.enableExceptions(FloatingPointControl.divByZeroException 945 | FloatingPointControl.overflowException); 946 assert(ctrl.enabledExceptions == 947 (FloatingPointControl.divByZeroException 948 | FloatingPointControl.overflowException)); 949 950 ctrl.rounding = FloatingPointControl.roundUp; 951 assert(FloatingPointControl.rounding == FloatingPointControl.roundUp); 952 } 953 ensureDefaults(); 954 } 955 956 @safe unittest // rounding 957 { 958 import std.meta : AliasSeq; 959 960 static T addRound(T)(uint rm) 961 { 962 pragma(inline, false); 963 FloatingPointControl fpctrl; 964 fpctrl.rounding = rm; 965 T x = 1; 966 x = forceAddOp(x, 0.1L); 967 return x; 968 } 969 970 static T subRound(T)(uint rm) 971 { 972 pragma(inline, false); 973 FloatingPointControl fpctrl; 974 fpctrl.rounding = rm; 975 T x = -1; 976 x = forceSubOp(x, 0.1L); 977 return x; 978 } 979 980 static foreach (T; AliasSeq!(float, double, real)) 981 {{ 982 /* Be careful with changing the rounding mode, it interferes 983 * with common subexpressions. Changing rounding modes should 984 * be done with separate functions that are not inlined. 985 */ 986 987 { 988 T u = addRound!(T)(FloatingPointControl.roundUp); 989 T d = addRound!(T)(FloatingPointControl.roundDown); 990 T z = addRound!(T)(FloatingPointControl.roundToZero); 991 992 assert(u > d); 993 assert(z == d); 994 } 995 996 { 997 T u = subRound!(T)(FloatingPointControl.roundUp); 998 T d = subRound!(T)(FloatingPointControl.roundDown); 999 T z = subRound!(T)(FloatingPointControl.roundToZero); 1000 1001 assert(u > d); 1002 assert(z == u); 1003 } 1004 }} 1005 } 1006 1007 } // FloatingPointControlSupport 1008 1009 version (StdUnittest) 1010 { 1011 // These helpers are intended to avoid constant propagation by the optimizer. 1012 pragma(inline, false) private @safe 1013 { 1014 T forceAddOp(T)(T x, T y) { return x + y; } 1015 T forceSubOp(T)(T x, T y) { return x - y; } 1016 T forceMulOp(T)(T x, T y) { return x * y; } 1017 T forceDivOp(T)(T x, T y) { return x / y; } 1018 } 1019 }